from flask_restplus import Resource, Namespace, fields, marshal, reqparse, Model
from common.decorators import catch_error, arguments_parser
from app.product.spu import service
from app.user.decorators import token_required
from common.responses import ok
from flask import g
from common.response_model import base_model, pagination_model
from common.request_parser import get_parser as base_get_parser
from app.product.category.controller import category_model
from app.product.spu import dao
from app.product.sku.controller import sku_model, sku_unit_price_model
from app.product.unit.controller import unit_model
from app.file.controller import file_model
from app.file.model import FileRelation, File
from common import constant
from app.company.model import Company
from app.product.sku.model import Sku
from app.product.spu.service import generate_sku_sn
from app.product.spu.model import Spu
from app.product.brand.controller import brand_model
from common import errors
from app.operation_log.model import OperationLog

api = Namespace('spu')


# ---------- model ----------
def get_main_unit_from_spu_to_units(spu_to_units):
    for ele in spu_to_units:
        if ele.is_base_unit:
            return ele.unit


def get_img_files_from_spu(spu):
    obj_to_files = FileRelation.query.filter_by_company_id(
        belong_id=spu.id,
        belong_type=constant.FileBelongType.SPU).all()
    files = []
    for ele in obj_to_files:
        file = File.query.filter_by(id=ele.file_id).first()
        # 过滤图片
        files.append(file)
    return files


def get_spec_attrs_str_from_skus(skus):
    # todo 应该是笛卡尔积乘出来的， 直接去 sku 里获取  属性1/属性2, 属性1/属性3
    if not skus:
        return ''
    spec_attrs = []
    for sku in skus:
        if not sku.spec_attr_ele:
            continue
        attrs = []
        for k, v in sku.spec_attr_ele.items():
            attrs.append(v)
        spec_attrs.append('/'.join(attrs))
    return ','.join(spec_attrs)


def get_sku_sns_from_skus(skus):
    if not skus:
        return []
    sku_sns = []
    for sku in skus:
        sku_sns.append(sku.sku_sn)
    return sku_sns


def get_warning_number_low_from_spu(spu):
    return spu.skus[0].warning_number_low


# 详情页
spu_model_for_list = base_model.inherit('spu_model_for_list', {
    'spec_attrs_str': fields.String(attribute=lambda spu: get_spec_attrs_str_from_skus(spu.skus)),
    'sku_sns': fields.List(fields.String(), attribute=lambda spu: get_sku_sns_from_skus(spu.skus)),
    'barcode': fields.String(),
    'spec_string': fields.String(description='型号规格'),
    'images': fields.List(fields.Nested(file_model), attribute=lambda spu: get_img_files_from_spu(spu)),
    'brand': fields.Nested(brand_model),
    'category': fields.Nested(category_model, skip_none=True),
    'unit': fields.Nested(unit_model, attribute=lambda spu: get_main_unit_from_spu_to_units(spu.spu_to_units)),
    # 'purchase_price': fields.Float(attribute=lambda spu: spu.skus[0].sku_unit_prices[0].purchase_price),
    # 'sale_price': fields.Float(attribute=lambda spu: spu.skus[0].sku_unit_prices[0].sale_price),
    'exp': fields.String(),
    'warning_number_low': fields.Float(attribute=lambda spu: get_warning_number_low_from_spu(spu)),
    'warning_number_high': fields.Float(attribute=lambda spu: spu.skus[0].warning_number_high),
})

# def get_unit_price_from_spu(spu):
#     return spu.skus[0].sku_unit_prices
spu_to_unit_model = Model('spu_to_unit_model', {
    'id': fields.Integer(description='编号'),
    'spu_id': fields.Integer(description='商品编号'),
    'unit_id': fields.Integer(description='单位编号'),
    'is_base_unit': fields.Boolean(description='是否基本单位'),
    'unit': fields.Nested(unit_model),
    'num_of_base_unit': fields.Float(description='基本单位数量'),
})


def get_sku_unit_prices_from_skus(skus):
    if not skus:
        return []
    sku_unit_prices = []
    for sku in skus:
        sku_unit_prices.extend(sku.sku_unit_prices)
    return sku_unit_prices


def get_spu_to_units_from_spu(spu_to_units):
    filter_spu_to_units = []
    for ele in spu_to_units:
        # 筛选出非基本单位
        if not ele.is_base_unit:
            filter_spu_to_units.append(ele)
    return filter_spu_to_units


spu_model_for_detail = spu_model_for_list.inherit('spu_model_for_detail', {
    'is_multiple_spec': fields.Boolean(),
    'spec_attrs': fields.Raw(),  # 商品规格
    # 'spu_to_units': fields.Nested(spu_to_unit_model, attribute=lambda spu:
    # filter(lambda spu_to_unit: (not spu_to_unit.is_base_unit), spu.spu_to_units)),

    'spu_to_units': fields.Nested(spu_to_unit_model, attribute=lambda spu: get_spu_to_units_from_spu(spu.spu_to_units)),
    'skus': fields.List(fields.Nested(sku_model)),
    'is_multiple_spec_price': fields.Boolean(),
    # 去获取第一个规格的价格
    'unit_prices': fields.List(fields.Nested(sku_unit_price_model),
                               attribute=lambda spu: spu.skus[0].sku_unit_prices),
    'sku_unit_prices': fields.List(fields.Nested(sku_unit_price_model),
                                   attribute=lambda spu: get_sku_unit_prices_from_skus(spu.skus)),
})

spu_paginate_model = pagination_model.inherit('spu_paginate_model', {
    "items": fields.Nested(spu_model_for_list)
})

# ---------- create parser ----------
create_parser = reqparse.RequestParser()
create_parser.add_argument('is_multiple_spec', type=bool, required=True, help='是否开启多规格')
create_parser.add_argument('is_active', type=bool, choices=[True, False], help='是否启用')
create_parser.add_argument('name', type=str, required=True, help='商品名称')
create_parser.add_argument('sku_sn', type=str, help='商品货号')
create_parser.add_argument('spec_string', type=str, help='型号规格')
create_parser.add_argument('category_id', type=int, help='商品分类')
create_parser.add_argument('brand_id', type=int, help='品牌')
create_parser.add_argument('unit_id', type=int, help='主单位')
create_parser.add_argument('spu_to_units', type=dict, help='辅助单位', action='append')
create_parser.add_argument('exp', type=str, help='保质期')
create_parser.add_argument('warning_number_low', type=float, help='库存预警下限')
create_parser.add_argument('warning_number_high', type=float, help='库存预警上限')
create_parser.add_argument('barcode', type=str, help='条形码')
create_parser.add_argument('notes', type=str, help='备注')
create_parser.add_argument('spec_attrs', type=dict, help='规格属性', action='append')
create_parser.add_argument('skus', type=dict, help='单品信息', action='append')
create_parser.add_argument('is_multiple_spec_price', type=bool, help='是否开启多价格')
create_parser.add_argument('unit_prices', type=dict, help='价格信息(不开启多价格)', action='append')
create_parser.add_argument('sku_unit_prices', type=dict, help='价格信息', action='append')
create_parser.add_argument('image_ids', type=int, help='图片id', action='append', default=[])

# ---------- get parser ----------
get_parser = base_get_parser.copy()
get_parser.add_argument('category_id', type=int, help='按类别筛选，会筛选所有子类')
get_parser.replace_argument('order_by', dest='sorted_by', type=str, choices=[
    'name', 'id', 'sku_sn', 'num', 'buy_price', 'sell_price', 'create_datetime', 'update_datetime'
], default='id', help='sorted_by')

# ---------- put parser ----------
batch_put_parser = reqparse.RequestParser()
batch_put_parser.add_argument('ids_list', type=int, action='append', required=True)
batch_put_parser.add_argument('is_active', type=bool, choices=[True, False], help='是否启用')

# ---------- delete parser ----------
batch_delete_parser = reqparse.RequestParser()
batch_delete_parser.add_argument('ids_list', type=int, action='append', required=True)


class SpusResource(Resource):
    @catch_error
    @token_required
    @api.expect(create_parser)
    def post(self):
        """创建"""
        args = create_parser.parse_args()

        new_obj = service.create_spu(**args)
        return ok(data=marshal(new_obj, spu_model_for_detail),
                  message='create success')

    @catch_error
    @token_required
    @api.expect(get_parser)
    def get(self):
        """获取列表"""
        args = get_parser.parse_args()
        pagination = dao.get_by_page(**args)
        # print("pagination", pagination)

        new_items = []
        for ele in pagination['items']:
            spu = Spu.query.filter_by(id=ele['id']).first()
            # marshal 放进去就好
            new_items.append(marshal(spu, spu_model_for_list))

        pagination['items'] = new_items
        return ok(data=pagination,
                  message='get success')

    # 批量删除
    @catch_error
    @token_required
    @api.expect(batch_delete_parser)
    def delete(self):
        args = batch_delete_parser.parse_args()
        delete_ids = Spu.hard_delete_many(ids=args.get("ids_list"), commit=True)
        # delete_ids = service.delete_many(g.user_id, args.get("ids_list"))
        return ok(
            data={
                'ids_list': delete_ids
            },
            message='delete success')

    # 批量修改
    @catch_error
    @token_required
    @api.expect(batch_put_parser)
    def put(self):
        args = batch_put_parser.parse_args()
        # 如果为空，则不要更新，删除None
        for key in list(args.keys()):
            if args.get(key) is None:
                del args[key]
        ids_list = Spu.update_many(
            ids=args.pop("ids_list"), **args
        )
        # update_ids_list = service.batch_update(g.user_id, args.pop("ids_list"), **args)
        return ok(
            data={
                'ids_list': ids_list
            },
            message='update success')


# ---------- put parser ----------
put_parser = create_parser.copy()
put_parser.remove_argument('sku_sn')


class SpuResource(Resource):
    @catch_error
    @token_required
    def get(self, obj_id):
        """获取"""
        obj = Spu.query.filter_by(id=obj_id).first()
        # 获取商品单位信息
        # spu_to_unit = dao.get_spu_to_units(obj_id)
        # obj.spu_to_unit = spu_to_unit

        # obj = service.get(g.user_id, obj_id)
        return ok(data=marshal(obj, spu_model_for_detail),
                  message='get success')

    @catch_error
    @token_required
    @api.expect(put_parser)
    def put(self, obj_id):
        """更新 基本信息 规格属性 单位"""
        args = put_parser.parse_args()
        obj = service.update_spu(obj_id, **args)
        return ok(data=marshal(obj, spu_model_for_detail),
                  message='update success')

    @catch_error
    @token_required
    def delete(self, obj_id):
        """获取"""
        obj = Spu.query.filter_by(id=obj_id).first()
        delete_id = obj.hard_delete(commit=True)
        # todo 是否删除关联的sku单品
        # delete_id = service.delete(g.user_id, obj_id)
        return ok(
            data={
                'id': delete_id
            },
            message='delete success')


get_validate_sku_sn_parser = reqparse.RequestParser()
get_validate_sku_sn_parser.add_argument('num', type=int, default=10)


class ValidateSkuSnResource(Resource):
    @catch_error
    @token_required
    @api.expect(get_validate_sku_sn_parser)
    def get(self):
        """获取多个有效的货号"""
        args = get_validate_sku_sn_parser.parse_args()
        num_of_need = args.get('num')
        company_id = g.company_id
        company = Company.query.filter_by(id=company_id).first()
        success_sku_sns = []
        num_of_try = 0
        while len(success_sku_sns) < num_of_need:
            if num_of_try >= 500:
                break
            index = company.auto_sku_sn_start_index + num_of_try
            one_sku_sn = generate_sku_sn(company.auto_sku_sn_prefix, index, company.auto_sku_sn_len)
            if not Sku.query.filter_by_company_id(sku_sn=one_sku_sn).first():
                success_sku_sns.append(one_sku_sn)
            num_of_try += 1
        return ok(
            data=success_sku_sns,
            message='get success'
        )


class IsSkuSnExistResource(Resource):
    @token_required
    @catch_error
    @arguments_parser
    def get(self):
        result = True if Sku.query.filter_by_company_id(sku_sn=g.args["sku_sn"]).first() else False
        return ok(data={"is_sku_sn_exist": result}, message="check finished")


action_put_parser = reqparse.RequestParser()
action_put_parser.add_argument('action', type=str, required=True, help='action',
                               choices=constant.OperationAction.PUT_ALL)


class SpuActionResource(Resource):
    @catch_error
    @token_required
    @api.expect(action_put_parser)
    def put(self, obj_id):
        """激活/禁用"""
        args = action_put_parser.parse_args()
        action = args.pop('action')

        obj = Spu.query.filter_by(id=obj_id).first()
        if action == constant.OperationAction.ACTIVE:
            obj.update(is_active=True, commit=True)
        elif action == constant.OperationAction.DE_ACTIVE:
            obj.update(is_active=False, commit=True)
        else:
            raise errors.BadRequestError('错误的操作类型')
        OperationLog.create(
            operation_log_type=constant.OperationLogType.SPU,
            parent_id=obj.id,
            action_str=action,
            notes=args.get('notes'),
            commit=True
        )
        return ok(data=marshal(obj, spu_model_for_detail),
                  message='action success')


# --------- route ----------
api.add_resource(SpusResource, '/spus')
api.add_resource(SpuResource, '/spus/<int:obj_id>')

# todo 从update中提出action, 启用，停用, 批量启用，批量停用， 批量修改
api.add_resource(SpuActionResource, '/spus/<int:obj_id>/action')

# 获取多个有效的货号
api.add_resource(ValidateSkuSnResource, '/validate_sku_sns')

# 校验货号是否已存在
api.add_resource(IsSkuSnExistResource, '/is_sku_sn_exist')

# 注册文档
api.models[spu_model_for_detail.name] = spu_model_for_detail

if __name__ == '__main__':
    print(generate_sku_sn('sn-', 1, 6))
    pass
